package com.example.Windturbine.Listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.Windturbine.Config.RedisConfig;
import com.example.Windturbine.Controller.ViewObject.Excel.PartExcel;
import com.example.Windturbine.Dao.ProductDOMapper;
import com.example.Windturbine.Dao.ProductInfoDOMapper;
import com.example.Windturbine.DataObject.ProductDO;
import com.example.Windturbine.DataObject.ProductInfoDO;
import com.example.Windturbine.Error.BusinessException;
import com.example.Windturbine.Error.EmBusinessError;
import com.example.Windturbine.Service.ProductService;
import com.example.Windturbine.SimilarLearning.AutoAddDataUtil;
import com.example.Windturbine.SimilarLearning.BP;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author 丁永新
 * @Date 2021/12/17
 */
public class ExcelListener extends AnalysisEventListener<PartExcel> {

    ProductService productService;

    Integer userid;

    Integer productid;

    ProductInfoDOMapper productInfoDOMapper;

    ProductDOMapper productDOMapper;

    BP bp;

    RedisTemplate redisTemplate;

    AutoAddDataUtil util=new AutoAddDataUtil();

    public List<String> orders=new ArrayList<>();

    public ExcelListener(ProductService productService, Integer productid, Integer userid, ProductInfoDOMapper info, ProductDOMapper productDOMapper) {
        this.productService=productService;
        this.userid=userid;
        this.productid=productid;
        this.productInfoDOMapper=info;
        this.productDOMapper=productDOMapper;
    }


    public ExcelListener(ProductService productService, Integer productid, Integer userid, ProductInfoDOMapper info, ProductDOMapper productDOMapper, BP bp, RedisTemplate redisTemplate) {
        this.productService=productService;
        this.userid=userid;
        this.productid=productid;
        this.productInfoDOMapper=info;
        this.productDOMapper=productDOMapper;
        this.bp=bp;
        this.redisTemplate=redisTemplate;
    }



    //存入数据库
    @SneakyThrows
    @Override
    public void invoke(PartExcel productInfoVO, AnalysisContext analysisContext) {
        try{
            ProductInfoDO productInfoDO=convertFromVO(productInfoVO);
            //自动补全数据
            if(bp==null){
                productInfoDO=autoMakeDate(productInfoDO);
            }else{
                productInfoDO=BPMakeDate(productInfoDO);
            }
            orders.add(productInfoDO.getOrderInfo());
            //System.out.println("读取excel中"+productInfoDO.getNameInfo());
            productService.submitBom(productInfoDO);
        }catch (Exception e){
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR,"Excel文件格式错误!");
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

    private ProductInfoDO convertFromVO(PartExcel productInfoVO){
        ProductInfoDO productInfoDO=new ProductInfoDO();
        BeanUtils.copyProperties(productInfoVO,productInfoDO);
        productInfoDO.setUserId(this.userid);
        productInfoDO.setProductId(this.productid);
        return productInfoDO;
    }


    //利用神经网络求解空缺数据
    public ProductInfoDO BPMakeDate(ProductInfoDO productInfoDO) throws BusinessException {
        String comment="";
        //先判断该数据是否有空缺
        String orderInfo = productInfoDO.getOrderInfo();
        String codenameInfo = productInfoDO.getCodenameInfo();
        String nameInfo = productInfoDO.getNameInfo();
        String serialInfo = productInfoDO.getSerialInfo();
        String functionInfo = productInfoDO.getFunctionInfo();
        String parameterInfo = productInfoDO.getParameterInfo();
        Integer numeveryInfo = productInfoDO.getNumeveryInfo();
        String materialInfo = productInfoDO.getMaterialInfo();
        Integer weighteveryInfo = productInfoDO.getWeighteveryInfo();
        BigDecimal weighttotalInfo = productInfoDO.getWeighttotalInfo();
        String commentsInfo = productInfoDO.getCommentsInfo();
        String originInfo = productInfoDO.getOriginInfo();
        Integer distanceInfo = productInfoDO.getDistanceInfo();
        String transportstyleInfo = productInfoDO.getTransportstyleInfo();
        String manufacturestyleInfo = productInfoDO.getManufacturestyleInfo();
        Integer recycleInfo = productInfoDO.getRecycleInfo();
        //有数据缺失
        if(nameInfo.equals("null")||serialInfo.equals("null")||functionInfo.equals("null")||originInfo.equals("null")
                ||parameterInfo.equals("null")|| materialInfo.equals("null")||weighttotalInfo.equals(new BigDecimal(0))
                ||transportstyleInfo.equals("null")||distanceInfo.equals(0)||manufacturestyleInfo.equals("null") ||recycleInfo.equals(0)){


            //获取材料库中的材料信息
            List<ProductInfoDO> partsInfos = (List<ProductInfoDO>)redisTemplate.opsForValue().get("partsInfo");
            if(partsInfos==null) {
                partsInfos = productInfoDOMapper.selectByUserId(userid);
                redisTemplate.opsForValue().set("partsInfo",partsInfos);
                redisTemplate.expire("partsInfo",10, TimeUnit.MINUTES);
            }

            PriorityQueue<double[]> queue=new PriorityQueue<double[]>((a,b)->{
                return b[0]-a[0]>=0?1:-1;
                //return (int)(-b[0]+a[0]);
            });
            //分别计算该零件与所有零件的相似度
            for(int i=0;i<partsInfos.size();i++){
                //计算11项信息的相似度
                double v1=0d,v2=0d,v3=0d,v4=0d,v5=0d,v6=0d,v7=0d,v8=0d,v9=0d,v10=0d,v11=0d;
                if(!nameInfo.equals("null")) v1 = util.wordHandle(nameInfo, partsInfos.get(i).getNameInfo());
                if(!serialInfo.equals("null")) v2 = util.wordHandle(serialInfo, partsInfos.get(i).getSerialInfo());
                if(!functionInfo.equals("null")) v3 = util.wordHandle(functionInfo, partsInfos.get(i).getFunctionInfo());
                if(!originInfo.equals("null")) v4 = util.wordHandle(originInfo, partsInfos.get(i).getOriginInfo());
                if(!parameterInfo.equals("null")) v5 = util.wordHandle(parameterInfo, partsInfos.get(i).getParameterInfo());
                if(!materialInfo.equals("null")) v6 = util.wordHandle(materialInfo, partsInfos.get(i).getMaterialInfo());
                if(!weighttotalInfo.equals(0)) v7 = util.numHandle(weighttotalInfo, partsInfos.get(i).getWeighttotalInfo());
                if(!transportstyleInfo.equals("null")) v8 = util.wordHandle(transportstyleInfo, partsInfos.get(i).getTransportstyleInfo());
                if(!distanceInfo.equals(0)) v9 = util.numHandle(new BigDecimal(distanceInfo), new BigDecimal(partsInfos.get(i).getDistanceInfo()));
                if(!manufacturestyleInfo.equals("null")) v10 = util.wordHandle(manufacturestyleInfo, partsInfos.get(i).getNameInfo());
                if(!recycleInfo.equals(0)) v11 = util.numHandle(new BigDecimal(recycleInfo),new BigDecimal(partsInfos.get(i).getRecycleInfo()));
                double[] input={v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11};
                double[] ouput=new double[2];
                bp.predict(input,ouput);
                double[] res=new double[2];
                res[0]=ouput[0];
                res[1]=i;
                queue.add(res);
            }

            //获取最相似的零件序号
            double[] peek = queue.peek();
            ProductInfoDO mostSimilar = partsInfos.get((int) peek[1]);
            //填充数据
            if(nameInfo.equals("null")){
                productInfoDO.setNameInfo(mostSimilar.getNameInfo());
                comment+="名字、";
            }
            if( serialInfo.equals("null")){
                productInfoDO.setSerialInfo(mostSimilar.getSerialInfo());
                comment+="系列号、";
            }
            if(functionInfo.equals("null")){
                productInfoDO.setFunctionInfo(mostSimilar.getFunctionInfo());
                comment+="功能、";
            }
            if(originInfo.equals("null")){
                productInfoDO.setOriginInfo(mostSimilar.getOriginInfo());
                comment+="产地、";
            }
            if(parameterInfo.equals("null")) {
                productInfoDO.setParameterInfo(mostSimilar.getParameterInfo());
                comment+="参数、";
            }
            if(materialInfo.equals("null")){
                productInfoDO.setMaterialInfo(mostSimilar.getMaterialInfo());
                comment+="材料、";
            }
            if(weighttotalInfo.equals(new BigDecimal(0))){
                productInfoDO.setNameInfo(mostSimilar.getNameInfo());
                comment+="总重、";
            }
            if(transportstyleInfo.equals("null")) {
                productInfoDO.setTransportstyleInfo(mostSimilar.getTransportstyleInfo());
                comment+="运输方式、";
            }
            if(distanceInfo.equals(0)) {
                productInfoDO.setDistanceInfo(mostSimilar.getDistanceInfo());
                comment+="运输距离、";
            }
            if(manufacturestyleInfo.equals("null")){
                productInfoDO.setManufacturestyleInfo(mostSimilar.getManufacturestyleInfo());
                comment+="工艺方式、";
            }
            if(recycleInfo.equals(0)) {
                productInfoDO.setRecycleInfo(mostSimilar.getRecycleInfo());
                comment+="回收比例、";
            }

            comment=comment.substring(0,comment.length()-1);

            productInfoDO.setCommentsInfo(comment+" 信息神经网络已智能补全！");
        }

        //返回填补好的数据
        return productInfoDO;
    }






    //根据概率来求解缺失数据
    public ProductInfoDO autoMakeDate(ProductInfoDO productInfoDO) throws BusinessException {
        List<ProductInfoDO> seriallist=new ArrayList<>();
        List<ProductInfoDO> namelist=new ArrayList<>();
        List<ProductInfoDO> functionlist=new ArrayList<>();
        String comment="";

        if(productInfoDO.getSerialInfo()!=null||productInfoDO.getSerialInfo()!=""){
            seriallist=productInfoDOMapper.selectBySerialAndUserId(productInfoDO.getSerialInfo(), userid);
        }

        if(productInfoDO.getNameInfo()!=null||productInfoDO.getNameInfo()!=""){
            namelist=productInfoDOMapper.selectByNameAndUserId(productInfoDO.getNameInfo(), userid);
        }

        if(productInfoDO.getFunctionInfo()!=null||productInfoDO.getFunctionInfo()!=""){
            functionlist=productInfoDOMapper.selectByFunctionAndUserId(productInfoDO.getFunctionInfo(), userid);
        }




        //1 序号没有
        String orderInfo = productInfoDO.getOrderInfo();
        if(orderInfo.equals("")||orderInfo==""||orderInfo==null){
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR,"序号缺失 无法计算填补信息后上传!");
        }

        //2 名称没有
        String name=productInfoDO.getNameInfo();
        if(name.equals("null")||name==""||name==null){
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR,"零件名称缺失 无法计算填补信息后上传!");
        }



        //3 功能没有
        String functionInfo = productInfoDO.getFunctionInfo();
        if(functionInfo.equals("null")||functionInfo==""||functionInfo==null){

            if(seriallist!=null&&seriallist.size()!=0){
                functionInfo=seriallist.get(0).getFunctionInfo();

                //flag=true;

            }else if(namelist!=null&&namelist.size()!=0){
                HashMap<String,Integer> map=new HashMap<>();
                for(int i=0;i<namelist.size();i++){
                    if(map.containsKey(namelist.get(i).getFunctionInfo())){
                        Integer n=map.get(namelist.get(i).getFunctionInfo());
                        map.put(functionlist.get(i).getFunctionInfo(),n+1);
                    }else{
                        map.put(functionlist.get(i).getFunctionInfo(),0);
                    }
                }
                List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet()); //转换为list
                //自定义排序 按照value的值降序排列
                Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
                    @Override
                    public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                        return o2.getValue().compareTo(o1.getValue());
                    }
                });
                functionInfo=list.get(0).getKey();
                //comment.append("功能 ");
                //flag=true;

            }else{
                functionInfo="未知";
                //.append("功能 ");
                //flag=true;
            }
            comment+="功能、";
            productInfoDO.setFunctionInfo(functionInfo);
        }








        //4 重量
        Integer weighteveryInfo = productInfoDO.getWeighteveryInfo();
        Integer numeveryInfo = productInfoDO.getNumeveryInfo();
        BigDecimal weighttotalInfo = productInfoDO.getWeighttotalInfo();


        if((weighteveryInfo==0||numeveryInfo==0)&&weighttotalInfo.equals(new BigDecimal(0))){

            if(seriallist!=null&&seriallist.size()!=0){
                weighttotalInfo=seriallist.get(0).getWeighttotalInfo();
                //comment.append("重量 ");
                //flag=true;

            }else if(namelist!=null&&namelist.size()!=0){
                BigDecimal weight=new BigDecimal(0);
                for(int i=0;i<namelist.size();i++){
                    weight=weight.add(namelist.get(i).getWeighttotalInfo());
                }
                weighttotalInfo=weight.divide(new BigDecimal(namelist.size()),2, BigDecimal.ROUND_HALF_DOWN);
                //comment.append("重量 ");
                //flag=true;

            }else{
                ProductDO productDO = productDOMapper.selectByPrimaryKey(productid);
                Integer weight1 = productDO.getWeight();
                weighttotalInfo=new BigDecimal(weight1).divide(new BigDecimal(20));
                //comment.append("重量 ");
                //flag=true;
            }
            comment+="重量、";
            productInfoDO.setWeighttotalInfo(weighttotalInfo);
        }




        //5 材料
        String material=productInfoDO.getMaterialInfo();


        if(material.equals("null")||material=="null"||material==null){
            //搜索逻辑
            //1 根据零件型号搜素
            if(seriallist!=null&&seriallist.size()!=0){
                material=seriallist.get(0).getMaterialInfo();
                //comment.append("材料 ");
                //flag=true;
            }else if(namelist!=null&&namelist.size()!=0){

                HashMap<String,Integer> map=new HashMap<>();
                //统计功能中材料使用最多的那种
                for(int i=0;i<namelist.size();i++){
                    if(map.containsKey(namelist.get(i).getMaterialInfo())){
                        Integer n=map.get(namelist.get(i).getMaterialInfo());
                        map.put(functionlist.get(i).getMaterialInfo(),n+1);
                    }else{
                        map.put(functionlist.get(i).getMaterialInfo(),0);
                    }
                }
                List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet()); //转换为list
                //自定义排序 按照value的值降序排列
                Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
                    @Override
                    public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                        return o2.getValue().compareTo(o1.getValue());
                    }
                });
                material=list.get(0).getKey();
                //comment.append("材料 ");
                //flag=true;
            }else if(functionlist!=null&&functionlist.size()!=0){
                HashMap<String,Integer> map=new HashMap<>();
                //统计功能中材料使用最多的那种
                for(int i=0;i<functionlist.size();i++){
                    if(map.containsKey(functionlist.get(i).getMaterialInfo())){
                        Integer n=map.get(functionlist.get(i).getMaterialInfo());
                        map.put(functionlist.get(i).getMaterialInfo(),n+1);
                    }else{
                        map.put(functionlist.get(i).getMaterialInfo(),0);
                    }
                }
                List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet()); //转换为list
                //自定义排序 按照value的值降序排列
                Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
                    @Override
                    public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                        return o2.getValue().compareTo(o1.getValue());
                    }
                });
                material=list.get(0).getKey();
                //comment.append("材料 ");
                //flag=true;
            }else{
                material="钢";
                //comment.append("材料 ");
                //flag=true;
            }
            comment+="材料、";
            productInfoDO.setMaterialInfo(material);

        }




        //6 运输距离
        Integer distanceInfo = productInfoDO.getDistanceInfo();


        if(distanceInfo==null||distanceInfo==0){
            outer:
            if(seriallist!=null&&seriallist.size()!=0){
                HashMap<Integer,Integer> map=new HashMap<>();
                for(int i=0;i<seriallist.size();i++){
                    if(seriallist.get(i).getOriginInfo().equals(productInfoDO.getOriginInfo())){
                        distanceInfo=seriallist.get(0).getDistanceInfo();
                        break outer;
                    }
                    if(map.containsKey(seriallist.get(i).getDistanceInfo())){
                        Integer n=map.get(seriallist.get(i).getDistanceInfo());
                        map.put(seriallist.get(i).getDistanceInfo(),n+1);
                    }else{
                        map.put(seriallist.get(i).getDistanceInfo(),0);
                    }
                }
                List<Map.Entry<Integer, Integer>> list = new ArrayList<Map.Entry<Integer, Integer>>(map.entrySet()); //转换为list
                //自定义排序 按照value的值降序排列
                Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
                    @Override
                    public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                        return o2.getValue().compareTo(o1.getValue());
                    }
                });
                distanceInfo=seriallist.get(0).getDistanceInfo();

            }else if(namelist!=null&&namelist.size()!=0){

                Double total=new Double(0);
                for(int i=0;i<namelist.size();i++){
                    total =total+ namelist.get(0).getDistanceInfo();
                }
                double v = total / namelist.size();
                distanceInfo=(int)v;
                //comment.append("运输距离 ");
                //flag=true;

            }else{
                productInfoDO.setDistanceInfo(100);
                //comment.append("运输距离 ");
                //flag=true;
            }
            comment+="运输距离、";

            productInfoDO.setDistanceInfo(distanceInfo);

        }




        //7 运输方式
        String style=productInfoDO.getTransportstyleInfo();
        if(style.equals("null")||style=="null"||style==null){

            outer1:
            if(seriallist!=null&&seriallist.size()!=0){
                HashMap<String,Integer> map=new HashMap<>();
                for(int i=0;i<seriallist.size();i++){
                    if(seriallist.get(i).getTransportstyleInfo().equals(productInfoDO.getTransportstyleInfo())){
                        style=seriallist.get(0).getTransportstyleInfo();
                        break outer1;
                    }

                    if(map.containsKey(seriallist.get(i).getTransportstyleInfo())){
                        Integer n=map.get(seriallist.get(i).getTransportstyleInfo());
                        map.put(seriallist.get(i).getTransportstyleInfo(),n+1);
                    }else{
                        map.put(seriallist.get(i).getTransportstyleInfo(),0);
                    }
                }
                List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet()); //转换为list
                //自定义排序 按照value的值降序排列
                Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
                    @Override
                    public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                        return o2.getValue().compareTo(o1.getValue());
                    }
                });

                style=seriallist.get(0).getTransportstyleInfo();
            }else if(namelist!=null&&namelist.size()!=0){
                HashMap<String,Integer> map=new HashMap<>();
                for(int i=0;i<namelist.size();i++){
                    if(map.containsKey(namelist.get(i).getTransportstyleInfo())){
                        Integer n=map.get(namelist.get(i).getTransportstyleInfo());
                        map.put(functionlist.get(i).getTransportstyleInfo(),n+1);
                    }else{
                        map.put(functionlist.get(i).getTransportstyleInfo(),0);
                    }
                }
                List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet()); //转换为list
                //自定义排序 按照value的值降序排列
                Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
                    @Override
                    public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                        return o2.getValue().compareTo(o1.getValue());
                    }
                });
                style=list.get(0).getKey();
                //comment.append("运输方式 ");
                //flag=true;

            }else{
                style="10t卡车柴油";
                //comment.append("运输方式 ");
                //flag=true;
            }
            comment+="运输方式、";
            productInfoDO.setTransportstyleInfo(style);
        }


        //8 工艺方式
        String manstyel=productInfoDO.getManufacturestyleInfo();
        if(manstyel.equals("null")||manstyel=="null"||manstyel==null){

            if(seriallist!=null&&seriallist.size()!=0){
                manstyel=seriallist.get(0).getManufacturestyleInfo();
                //comment.append("工艺方式 ");
                //flag=true;
            }else if(namelist!=null&&namelist.size()!=0){
                HashMap<String,Integer> map=new HashMap<>();
                for(int i=0;i<namelist.size();i++){
                    if(map.containsKey(namelist.get(i).getManufacturestyleInfo())){
                        Integer n=map.get(namelist.get(i).getManufacturestyleInfo());
                        map.put(functionlist.get(i).getManufacturestyleInfo(),n+1);
                    }else{
                        map.put(functionlist.get(i).getManufacturestyleInfo(),0);
                    }
                }
                List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(map.entrySet()); //转换为list
                //自定义排序 按照value的值降序排列
                Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
                    @Override
                    public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                        return o2.getValue().compareTo(o1.getValue());
                    }
                });
                manstyel=list.get(0).getKey();
                //comment.append("工艺方式 ");
                //flag=true;
            }else{
                manstyel="切削粗加工-切削精加工";
                //comment.append("工艺方式 ");
                //flag=true;
            }
            comment+="工艺、";
            productInfoDO.setManufacturestyleInfo(manstyel);

        }


        //9 回收
        Integer recycleInfo = productInfoDO.getRecycleInfo();
        if(recycleInfo.equals(0)||recycleInfo==0||recycleInfo==null){

            if(seriallist!=null&&seriallist.size()!=0){
                recycleInfo=seriallist.get(0).getRecycleInfo();
            }else if(namelist!=null&&namelist.size()!=0){
                HashMap<Integer,Integer> map=new HashMap<>();
                for(int i=0;i<namelist.size();i++){
                    if(map.containsKey(namelist.get(i).getRecycleInfo())){
                        Integer n=map.get(namelist.get(i).getRecycleInfo());
                        map.put(functionlist.get(i).getRecycleInfo(),n+1);
                    }else{
                        map.put(functionlist.get(i).getRecycleInfo(),0);
                    }
                }
                List<Map.Entry<Integer, Integer>> list = new ArrayList<Map.Entry<Integer, Integer>>(map.entrySet()); //转换为list
                //自定义排序 按照value的值降序排列
                Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
                    @Override
                    public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                        return o2.getValue().compareTo(o1.getValue());
                    }
                });
                recycleInfo=list.get(0).getKey();
                //comment.append("回收百分比 ");
                //flag=true;
            }else{
                recycleInfo=5;
                //comment.append("回收百分比 ");
                //flag=true;
            }
            comment+="回收、";

            productInfoDO.setRecycleInfo(recycleInfo);
        }



        if(!comment.equals("")||comment!=""){
            comment=comment.substring(0,comment.length()-1);
            comment+="已补全！";
            productInfoDO.setCommentsInfo(comment);
        }



        return productInfoDO;
    }
}

